package com.rao.gmall.web.all.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rao.gmall.common.constant.RedisConst;
import com.rao.gmall.feign.cart.CartFeignClient;
import com.rao.gmall.feign.product.SkuInfoFeignClient;
import com.rao.gmall.feign.user.UserFeignClient;
import com.rao.gmall.model.cart.CartInfo;
import com.rao.gmall.model.user.UserAddress;
import com.rao.gmall.web.all.mapper.OrderMapper;
import com.rao.gmall.web.all.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author nzmxfan
 * @create 2021-08-20-19:33
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    CartFeignClient cartFeignClient;

    @Qualifier("wellPool")
    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    SkuInfoFeignClient skuInfoFeignClient;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    UserFeignClient userFeignClient;

    // 获取订单信息
    @Override
    public Map<String, Object> getOrderInfo(String userId) {
        // 创建一个 map 用于存储获取的值
        Map<String, Object> map = new HashMap<>();
        // 创建一个 num 累计所有商品，得到总数量
        final AtomicInteger[] totalNum = {new AtomicInteger(0)};
        // 创建一个 num 累计所有商品，得到总数量
        final BigDecimal[] totalAmount = {new BigDecimal(0.0)};

        // 创建一个请求属性，用于异步编排载入输入（异步编排使用的线程非当前线程，无法获取请求数据）
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

        // 1.远程获取所有的商品信息
        CompletableFuture<List<CartInfo>> listCompletableFuture = CompletableFuture.supplyAsync(() -> {
            // 异步线程把老请求内容放入，解决 feign 异步请求丢失请求头信息问题
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<CartInfo> cartList = cartFeignClient.getCartIsCheckList();
            return cartList;
        }, executor);

        // 2.获取商品最新价格、并计算总价  totalNum\ totalAmount \ detailArrayList \ userAddressList
        CompletableFuture<List<CartInfo>> attrCompletableFuture = listCompletableFuture.whenCompleteAsync((result, exception) -> {

            // 异步线程把老请求内容放入，解决 feign 异步请求丢失请求头信息问题
            RequestContextHolder.setRequestAttributes(requestAttributes);

            for (CartInfo cartInfo : result) {
                // 遍历并每个商品的 skuId
                Long skuId = cartInfo.getSkuId();
                // 查询商品的最新价格
                BigDecimal skuPrice = skuInfoFeignClient.getSkuPrice(skuId);

                cartInfo.setOrderPrice(skuPrice);

                // 获取商品的数量
                Integer skuNum = cartInfo.getSkuNum();
                totalNum[0].addAndGet(skuNum);

                //计算商品的总价
                BigDecimal multiply = skuPrice.multiply(new BigDecimal(skuNum.toString()));
                totalAmount[0] = totalAmount[0].add(multiply);

                map.put("totalNum",totalNum[0]);
                map.put("totalAmount", totalAmount[0]);
                map.put("detailArrayList", result);
            }
        }, executor);

        // 3.获取用户的收货地址
        CompletableFuture<Void> userAddresseCompletableFuture = CompletableFuture.runAsync(() -> {

            // 异步线程把老请求内容放入，解决 feign 异步请求丢失请求头信息问题
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<UserAddress> userAddresses = userFeignClient.getUserAddresses();
            map.put("userAddressList", userAddresses);
        }, executor);

        // 4.获取 tradeNo，避免重复提交
        CompletableFuture<Void> tradeNoCompletableFuture = CompletableFuture.runAsync(() -> {
            String token = UUID.randomUUID().toString().replace("-", "");
            redisTemplate.opsForValue().set(RedisConst.USER_UNREPEAT_TOKEN + userId, token, RedisConst.SKUKEY_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);
            map.put("tradeNo", token);
        }, executor);

        CompletableFuture.allOf(listCompletableFuture, attrCompletableFuture ,userAddresseCompletableFuture, tradeNoCompletableFuture)
                .join(); // 与 allOf.get() 作用一样，等所有异步线程执行结束才向下执行


        return map;
    }
}
